package asteroid

import (
	"fmt"
	"runtime/debug"

	"gddgame.cc/galaxy/utils/def"
)

type FilterHandler func(message Message, member def.Member)
type OnHandler func(message Message)
type SerializerHandler func() def.OptimizeSerializer

type Upstream interface {
	GetPayload() []byte
	GetEvent() interface{}
	GetFrom() string
	GetTarget() string
	GetOrigin() string
	Response(data interface{}, serializer ...def.AutoSerializer)
	IsCross() bool
	// notice: 尽量不要缓存该值，避免系统更新
	Cross() string
}
type Message interface {
	Upstream
	IsNil() bool
	Payload() ([]byte, error)
	List() []interface{}
	Data() interface{}
	Read(index int) interface{}
	UnSerialize(s def.OptimizeSerializer)
}
type message struct {
	b   []byte
	bag Bag
	s   def.OptimizeSerializer

	//def.Logger
	Upstream
}

func (message *message) release() {
	//fmt.Println("release", message)
	if message.s != nil {
		message.s.Release()
		message.s = nil
	}
	if message.b != nil {
		message.b = nil
	}
	message.bag.Clear()
}

func (message *message) bagUpdate() error {
	var err error
	if message.bag.Data == nil {
		b := message.Upstream.GetPayload()
		err = message.bag.Unmarshal(b)
	}
	return err
}

func (message *message) Payload() ([]byte, error) {
	var b []byte
	var err error
	if message.b != nil {
		goto Return
	}
	b = message.Upstream.GetPayload()
	if b != nil {
		message.b = b
		goto Return
	}
	b, err = message.get()
	if b != nil {
		message.b = b
		goto Return
	}
Return:
	return message.b, err
}

func (message *message) IsNil() bool {
	if message.s != nil {
		return false
	} else if message.b != nil {
		return false
	} else if message.bag.Data != nil {
		return false
	} else if message.GetPayload() != nil {
		return false
	} else {
		return true
	}
}
func (message *message) List() []interface{} {
	err := message.bagUpdate()
	if err != nil {
		fmt.Println("asteroid message list:", err)
	}
	return message.bag.Get()
}
func (message *message) Data() interface{} {
	err := message.bagUpdate()
	if err != nil {
		fmt.Println("asteroid message data:", err)
	}
	if len(message.bag.Data) == 0 {
		return nil
	}
	return message.bag.Data[0]
}
func (message *message) Read(index int) interface{} {
	err := message.bagUpdate()
	if err != nil {
		fmt.Println("asteroid message read:", err)
	}
	if len(message.bag.Data) < index+1 {
		return nil
	}
	return message.bag.Data[index]
}
func (message *message) UnSerialize(s def.OptimizeSerializer) {
	//fmt.Println("copy", message)
	if message.s != nil {
		s.Copy(message.s)
	} else {
		b, err := message.Payload()
		if err != nil {
			fmt.Println("[ Asteroid ] Message GetPayload:", err)
			debug.PrintStack()
		} else if err := s.Unmarshal(b); err != nil {
			fmt.Println("[ Asteroid ] Message unserialize:", err)
			debug.PrintStack()
		}
	}
}

func (message *message) set(data interface{}, response bool) {
	if data != nil {
		switch v := data.(type) {
		case def.OptimizeSerializer:
			v.Alloc()
			message.s = v
		case []byte:
			message.b = v
		case []interface{}:
			//fmt.Println("set []interface{}", v)
			if response {
				message.bag.Set(v)
			} else {
				message.bag.SetList(v)
			}
		case interface{}:
			//fmt.Println("set interface{}", v)
			message.bag.Set(v)
		}
	}
}

func (message *message) get() ([]byte, error) {
	if message.b != nil {
		return message.b, nil
	} else if message.s != nil {
		b, err := message.s.Marshal()
		if err != nil {
			return nil, err
		}
		return b, nil
	} else if message.bag.Data != nil {
		b, err := message.bag.Marshal()
		if err != nil {
			return nil, err
		}
		return b, nil
	} else {
		return nil, nil
	}
}

type Bag struct {
	b    []byte
	Data ISlice
}

func (p *Bag) Marshal() ([]byte, error) {
	return p.Data.MarshalMsg(p.b)
}

func (p *Bag) Unmarshal(b []byte) error {
	var err error
	if b == nil {
		p.Data = []interface{}{}
	} else {
		_, err = p.Data.UnmarshalMsg(b)
	}
	return err
}

func (p *Bag) Set(d ...interface{}) {
	p.Data = d
}

func (p *Bag) SetList(d []interface{}) {
	p.Data = d
}

func (p *Bag) Get() []interface{} {
	return p.Data
}

func (p *Bag) Clear() {
	p.Data = nil
	p.b = p.b[:0]
}
